#ifndef AER_BOUNDINGBOX_HPP_
#define AER_BOUNDINGBOX_HPP_

#include "aerDefs.hpp"
#include "aerOpenGL.hpp"//


namespace aer {

/// ========================================
/// + Defines an Axis-Aligned Bounding Box +
/// ========================================
class BoundingBox // : public BoundingVolume
{
  private:
    // !! not initialized !!
    Vector3 m_min;
    Vector3 m_max;
    Vector3 m_side;//
  
  
  public:
    BoundingBox() {}
    
    BoundingBox(const BoundingBox &bbox) 
        : m_min(bbox.m_min), m_max(bbox.m_max), m_side(bbox.m_side)
    {}
  
    BoundingBox(const size_t vertexCount, const float *vertices)
    {
      set(vertexCount, vertices);
    }
    
    BoundingBox(const Vector3& min, const Vector3 &max)
        : m_min(min), m_max(max)
    {
      computeSide();
    }
    
    
    // xxx temp xxx
    void render() const
    {
      glPushMatrix();
      
      const aer::Vector3 &pos   = getCenter();
      const aer::Vector3 &scale = getScale();
      
      glTranslatef( pos.x, pos.y, pos.z );
      glScalef( scale.x, scale.y, scale.z );
      
      glDisable( GL_CULL_FACE );
      glBegin( GL_QUADS );
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, +1.0f);
        glVertex3f(-1.0f, +1.0f, +1.0f);
        glVertex3f(-1.0f, +1.0f, -1.0f);
        
        glVertex3f(+1.0f, -1.0f, -1.0f);
        glVertex3f(+1.0f, -1.0f, +1.0f);
        glVertex3f(+1.0f, +1.0f, +1.0f);
        glVertex3f(+1.0f, +1.0f, -1.0f);
        
        glVertex3f(-1.0f, +1.0f, -1.0f);
        glVertex3f(-1.0f, +1.0f, +1.0f);
        glVertex3f(+1.0f, +1.0f, +1.0f);
        glVertex3f(+1.0f, +1.0f, -1.0f);
        
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, +1.0f);
        glVertex3f(+1.0f, -1.0f, +1.0f);
        glVertex3f(+1.0f, -1.0f, -1.0f);
        
        glVertex3f(-1.0f, -1.0f, +1.0f);
        glVertex3f(-1.0f, +1.0f, +1.0f);
        glVertex3f(+1.0f, +1.0f, +1.0f);
        glVertex3f(+1.0f, -1.0f, +1.0f);
        
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, +1.0f, -1.0f);
        glVertex3f(+1.0f, +1.0f, -1.0f);
        glVertex3f(+1.0f, -1.0f, -1.0f);
      glEnd(); 
      
      glPopMatrix();
    }
    
    void set(const size_t vertexCount, const float *vertices)
    {
      AER_ASSERT( vertices != NULL );
      
      m_min = Vector3( vertices[0], vertices[1], vertices[2]);
      m_max = m_min;
      
      for (size_t i=1; i<vertexCount; ++i)
      {
        const float *v = &vertices[3*i];
        
        if (v[0] < m_min.x) m_min.x = v[0];
        if (v[1] < m_min.y) m_min.y = v[1];
        if (v[2] < m_min.z) m_min.z = v[2];
        
        if (v[0] > m_max.x) m_max.x = v[0];
        if (v[1] > m_max.y) m_max.y = v[1];
        if (v[2] > m_max.z) m_max.z = v[2];
      }
      computeSide();
    }
    
    void addAABB(const BoundingBox &bbox)
    {
      m_min = glm::min( m_min, bbox.getMin());
      m_max = glm::max( m_max, bbox.getMax());
      computeSide();
    }
    
    //void addVertex(const Vector3 &vertex);
    
    
    const Vector3& getMin()  const { return m_min; }
    const Vector3& getMax()  const { return m_max; }
    const Vector3& getSide() const { return m_side; }

    Vector3 getScale()       const { return 0.5f*m_side; }
    Vector3 getCenter()      const { return 0.5f*(m_min+m_max); }

    
    const float getSurfaceArea() const { 
      return (m_side.x*m_side.y + m_side.x*m_side.z + m_side.y*m_side.z) * 2.0f;
    }
    
    
  private:
    void computeSide() { m_side = glm::abs(m_max - m_min); }
};
  
} // aer

#endif //AER_BOUNDINGBOX_HPP_
